﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.VisualBasic;

namespace zykControl
{

    /// <summary>本类可以在.NET环境下使用系统键盘与鼠标钩子</summary>
    public class ClsSystemHook
    {

        #region 定义结构

        private struct MouseHookStruct
        {
            public Point PT;
            public int Hwnd;
            public int WHitTestCode;
            public int DwExtraInfo;
        }

        private struct MouseLLHookStruct
        {
            public Point PT;
            public int MouseData;
            public int Flags;
            public int Time;
            public int DwExtraInfo;
        }

        private struct KeyboardHookStruct
        {
            public Keys vkCode;
            public int ScanCode;
            public int Flags;
            public int Time;
            public int DwExtraInfo;
        }

        #endregion

        #region API声明导入

        [DllImport("user32")]
        private static extern int SetWindowsHookExA(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
        [DllImport("user32")]
        private static extern int UnhookWindowsHookEx(int idHook);
        [DllImport("user32")]
        private static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
        [DllImport("user32")]
        private static extern int ToAscii(int uVirtKey, int uScancode, byte[] lpdKeyState, byte[] lpwTransKey, int fuState);
        [DllImport("user32")]
        private static extern int GetKeyboardState(byte[] pbKeyState);
        [DllImport("user32")]
        private static extern short GetKeyState(int vKey);

        private delegate int HookProc(int nCode, int wParam, IntPtr lParam);

        #endregion

        #region 常量声明

        private const int WH_MOUSE_LL = 14;
        private const int WH_KEYBOARD_LL = 13;
        private const int WH_MOUSE = 7;
        private const int WH_KEYBOARD = 2;
        private const int WM_MOUSEMOVE = 0x200;
        private const int WM_LBUTTONDOWN = 0x201;
        private const int WM_RBUTTONDOWN = 0x204;
        private const int WM_MBUTTONDOWN = 0x207;
        private const int WM_LBUTTONUP = 0x202;
        private const int WM_RBUTTONUP = 0x205;
        private const int WM_MBUTTONUP = 0x208;
        private const int WM_LBUTTONDBLCLK = 0x203;
        private const int WM_RBUTTONDBLCLK = 0x206;
        private const int WM_MBUTTONDBLCLK = 0x209;
        private const int WM_MOUSEWHEEL = 0x20A;
        private const int WM_KEYDOWN = 0x100;
        private const int WM_KEYUP = 0x101;
        private const int WM_SYSKEYDOWN = 0x104;
        private const int WM_SYSKEYUP = 0x105;
        private const byte VK_SHIFT = 0x10;
        private const byte VK_CAPITAL = 0x14;
        private const byte VK_NUMLOCK = 0x90;

        #endregion

        /// <summary>鼠标激活事件</summary>
        public event MouseEventHandler MouseActivity;
        /// <summary>键盘按下事件</summary>
        public event KeyEventHandler KeyDown;
        /// <summary>键盘输入事件</summary>
        public event KeyPressEventHandler KeyPress;
        /// <summary>键盘松开事件</summary>
        public event KeyEventHandler KeyUp;

        private int hMouseHook;
        private int hKeyboardHook;
        private static HookProc MouseHookProcedure;
        private static HookProc KeyboardHookProcedure;

        /// <summary>创建一个全局鼠标键盘钩子 (请使用Start方法开始监视)</summary>
        public ClsSystemHook()
        {
            // 留空即可
        }
        /// <summary>创建一个全局鼠标键盘钩子，决定是否安装钩子</summary>
        /// <param name="InstallAll">是否立刻挂钩系统消息</param>
        public ClsSystemHook(bool InstallAll)
        {
            if (InstallAll)
                StartHook(true, true);
        }
        /// <summary>创建一个全局鼠标键盘钩子，并决定安装钩子的类型</summary>
        /// <param name="InstallKeyboard">挂钩键盘消息</param>
        /// <param name="InstallMouse">挂钩鼠标消息</param>
        public ClsSystemHook(bool InstallKeyboard, bool InstallMouse)
        {
            StartHook(InstallKeyboard, InstallMouse);
        }
        /// <summary>析构函数</summary>
        ~ClsSystemHook()
        {
            UnHook(); // 卸载对象时反注册系统钩子
        }

        /// <summary>开始安装系统钩子</summary>
        /// <param name="InstallKeyboardHook">挂钩键盘消息</param>
        /// <param name="InstallMouseHook">挂钩鼠标消息</param>
        public void StartHook(bool InstallKeyboardHook = true, bool InstallMouseHook = false)
        {
            // 注册键盘钩子
            if (InstallKeyboardHook && hKeyboardHook == 0)
            {
                KeyboardHookProcedure = new HookProc(KeyboardHookProc);
                hKeyboardHook = SetWindowsHookExA(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
                if (hKeyboardHook == 0) // 检测是否注册完成
                {
                    UnHook(true, false); // 在这里反注册
                    throw new Win32Exception(Marshal.GetLastWin32Error()); // 报告错误
                }
            }
            // 注册鼠标钩子
            if (InstallMouseHook && hMouseHook == 0)
            {
                MouseHookProcedure = new HookProc(MouseHookProc);
                hMouseHook = SetWindowsHookExA(WH_MOUSE_LL, MouseHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
                if (hMouseHook == 0)
                {
                    UnHook(false, true);
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
        }
        /// <summary>立刻卸载系统钩子</summary>
        /// <param name="UninstallKeyboardHook">卸载键盘钩子</param>
        /// <param name="UninstallMouseHook">卸载鼠标钩子</param>
        /// <param name="ThrowExceptions">是否报告错误</param>
        public void UnHook(bool UninstallKeyboardHook = true, bool UninstallMouseHook = true, bool ThrowExceptions = false)
        {
            // 卸载键盘钩子
            if (hKeyboardHook != 0 && UninstallKeyboardHook)
            {
                int retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
                hKeyboardHook = 0;
                if (ThrowExceptions && retKeyboard == 0) // 如果出现错误，是否报告错误
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error()); // 报告错误
                }
            }
            // 卸载鼠标钩子
            if (hMouseHook != 0 && UninstallMouseHook)
            {
                int retMouse = UnhookWindowsHookEx(hMouseHook);
                hMouseHook = 0;
                if (ThrowExceptions && retMouse == 0)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
        }

        // 键盘消息的委托处理代码
        private int KeyboardHookProc(int nCode, int wParam, IntPtr lParam)
        {
            bool handled = false;
            if (nCode >= 0)
            {
                KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
                // 激活KeyDown
                if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) // 如果消息为按下普通键或系统键
                {
                    var e = new KeyEventArgs(MyKeyboardHookStruct.vkCode);
                    KeyDown?.Invoke(this, e); // 激活事件
                    handled = handled | e.Handled; // 是否取消下一个钩子
                }
                // 激活KeyUp
                if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
                {
                    var e = new KeyEventArgs(MyKeyboardHookStruct.vkCode);
                    KeyUp?.Invoke(this, e);
                    handled = handled | e.Handled;
                }
                // 激活KeyPress
                if (wParam == WM_KEYDOWN)
                {
                    bool isDownShift = (GetKeyState(VK_SHIFT) & 0x80) == 0x80;
                    bool isDownCapslock = GetKeyState(VK_CAPITAL) != 0;
                    var keyState = new byte[257];
                    GetKeyboardState(keyState);
                    var inBuffer = new byte[3];
                    if (ToAscii((int)MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.ScanCode, keyState, inBuffer, MyKeyboardHookStruct.Flags) == 1)
                    {
                        char key = Strings.Chr(inBuffer[0]);
                        if (isDownCapslock ^ isDownShift & char.IsLetter(key))
                        {
                            key = char.ToUpper(key);
                        }

                        var e = new KeyPressEventArgs(key);
                        KeyPress?.Invoke(this, e);
                        handled = handled | e.Handled;
                    }
                }
                // 取消或者激活下一个钩子
                if (handled)
                    return 1;
                else
                    return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
            }

            return 0;
        }
        // 鼠标消息的委托处理代码
        private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                MouseLLHookStruct mouseHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct));
                var moubut = MouseButtons.None; // 鼠标按键
                int mouseDelta = 0; // 滚轮值
                switch (wParam)
                {
                    case WM_LBUTTONDOWN:
                        {
                            moubut = MouseButtons.Left;
                            break;
                        }

                    case WM_RBUTTONDOWN:
                        {
                            moubut = MouseButtons.Right;
                            break;
                        }

                    case WM_MBUTTONDOWN:
                        {
                            moubut = MouseButtons.Middle;
                            break;
                        }

                    case WM_MOUSEWHEEL:
                        {
                            int @int = mouseHookStruct.MouseData >> 16 & 0xFFFF;
                            // 本段代码CLE添加，模仿C#的Short从Int弃位转换
                            if (@int > short.MaxValue)
                                mouseDelta = @int - 65536;
                            else
                                mouseDelta = @int;
                            break;
                        }
                }

                int clickCount = 0; // 单击次数
                if (moubut != MouseButtons.None)
                {
                    if (wParam == WM_LBUTTONDBLCLK || wParam == WM_RBUTTONDBLCLK || wParam == WM_MBUTTONDBLCLK)
                    {
                        clickCount = 2;
                    }
                    else
                    {
                        clickCount = 1;
                    }
                }

                var e = new MouseEventArgs(moubut, clickCount, mouseHookStruct.PT.X, mouseHookStruct.PT.Y, mouseDelta);
                MouseActivity?.Invoke(this, e);
            }

            return CallNextHookEx(hMouseHook, nCode, wParam, lParam); // 激活下一个钩子
        }

        /// <summary>键盘钩子是否无效</summary>
        public bool KeyHookInvalid
        {
            get
            {
                return hKeyboardHook == 0;
            }

            set
            {
                if (value)
                    UnHook(true, false);
                else
                    StartHook(true, false);
            }
        }
        /// <summary>鼠标钩子是否无效</summary>
        public bool MouseHookInvalid
        {
            get
            {
                return hMouseHook == 0;
            }

            set
            {
                if (value)
                    UnHook(false, true);
                else
                    StartHook(false, true);
            }
        }
    }
}